package task

import (
	"fmt"
	"reflect"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/item"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/orm"
)

type TaskData struct {
	env       *game.Environment
	GameTasks map[int]*GameTask

	languages map[string]*language
	taskModel *orm.Model
}

type language struct {
	Tasks map[int]*Task
}

func GetTaskData(env *game.Environment, extendType reflect.Type, insideType reflect.Type, itemData *ItemData) (*TaskData, error) {
	var err error
	data := &TaskData{env: env, languages: map[string]*language{}}

	if data.taskModel, _, data.GameTasks, err = getTaskData(env, extendType, insideType); err != nil {
		return nil, err
	}

	items := itemData.GetItem()
	for _, lang := range env.LanguageList {
		language := &language{}

		tasks := make(map[int]*Task, len(data.GameTasks))
		for _, task := range data.GameTasks {
			tasks[task.Id] = NewTask(task, lang, items)
		}
		language.Tasks = tasks
		data.languages[lang] = language
	}
	return data, nil
}

func getTaskData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*orm.Model, []GameTask, map[int]*GameTask, error) {
	var tasks []GameTask
	model, err := env.GetModel(GameTaskName)
	if err != nil {
		return nil, nil, nil, err
	}
	if _, err := model.Query().
		WhereSql("start_time is null or start_time <='" + time.Now().Format(time.RFC3339) + "'").
		WhereSql("expire_time is null or expire_time >='" + time.Now().Format(time.RFC3339) + "'").
		Find(&tasks); err != nil {
		return nil, nil, nil, err
	}
	taskMap := make(map[int]*GameTask, len(tasks))
	for k := range tasks {
		if extendType != nil {
			tasks[k].Extend = utils.ToType(tasks[k].Extend, extendType)
		}
		if insideType != nil {
			tasks[k].Inside = utils.ToType(tasks[k].Inside, insideType)
		}
		taskMap[tasks[k].Id] = &tasks[k]
	}
	return model, tasks, taskMap, nil
}

func (taskData *TaskData) GetTaskFromIds(ids []int, language string) ([]*Task, error) {
	data := taskData.languages[language]
	result := make([]*Task, len(ids))
	for index, id := range ids {
		result[index] = data.Tasks[id]
	}
	return result, nil
}

func (taskData *TaskData) GetTaskProcess(userId int, taskIds []int) (map[int]map[string]int32, error) {
	infos := make(map[int]map[string]int32, len(taskIds))
	for _, id := range taskIds {
		var info map[string]int32
		_ = taskData.env.Get(fmt.Sprintf("task:%d:%d", id, userId), &info)
		if info != nil {
			infos[id] = info
		} else {
			infos[id] = map[string]int32{}
		}
	}
	return infos, nil
}

func (taskData *TaskData) SaveTaskProcess(userId int, process map[int]map[string]int32, expire int) error {
	for id, p := range process {
		if err := taskData.env.SetExpire(fmt.Sprintf("task:%d:%d", id, userId), p, int(expire)); err != nil {
			return err
		}
	}
	return nil
}
